/**
 * 
 */
package com.senyint.sys.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.util.LinkedCaseInsensitiveMap;

import com.senyint.sys.utils.log.Log;

/**
 * @author WangNaiFei
 */
public class XmlBeanUtil {

	private String dateFormat = "yyyy-MM-dd HH:mm:ss.SSS";
	private String charset = "UTF-8";
	private String ignoreNull = "NORMAL";

	public static final String IGNORE_NORMAL = "NORMAL";
	public static final String IGNORE_NON_NULL = "NON_NULL";

	/**
	 * 
	 */
	public XmlBeanUtil() {
	}

	public String getDateFormat() {
		return dateFormat;
	}

	public void setDateFormat(String dateFormat) {
		this.dateFormat = dateFormat;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public String getIgnoreNull() {
		return ignoreNull;
	}

	public void setIgnore(String ignoreNull) {
		this.ignoreNull = ignoreNull;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Document obj2xml(Object obj) {
		Document doc = DocumentHelper.createDocument();
		doc.setXMLEncoding(charset);
		if (obj == null) {
			Element root = doc.addElement("ROOT");
			doc.setRootElement(root);
			root.setText("");
			Log.INSTANCE.error("the object is null");
		} else {
			if (obj instanceof Map) {
				Map m = (Map) obj;
				map2xml(m, doc);
			} else {
				bean2xml(obj, doc);
			}
		}
		return doc;
	}

	public void map2xml(Map<String, Object> m, Document doc) {
		if (m.size() == 1) {
			String key = m.keySet().iterator().next();
			Element root = doc.addElement(key);
			doc.setRootElement(root);
			obj2Element(m.get(key), root);
		} else {
			Element root = doc.addElement("ROOT");
			doc.setRootElement(root);
			root.setText("error. map mast have one entry as document root element");
			Log.INSTANCE.error("map mast have one entry as document root element");
		}
	}

	public void bean2xml(Object beanObject, Document doc) {
		Element root = doc.addElement(beanObject.getClass().getSimpleName());
		doc.setRootElement(root);
		obj2Element(beanObject, root);
	}

	public void obj2Element(Object obj, Element el) {
		if (obj == null) {
			if (ignoreNull.equals(IGNORE_NON_NULL)) {
				el.getParent().remove(el);
			} else {
				el.setText("");
			}
		} else if (obj instanceof java.lang.Boolean) {
			el.setText(obj.toString());
		} else if (obj instanceof java.lang.Integer) {
			el.setText(obj.toString());
		} else if (obj instanceof java.lang.Float) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.lang.Double) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.lang.Number) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.util.Date) {
			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
			el.setText(sdf.format((Date) obj));
		} else if (obj instanceof java.lang.String) {
			el.setText((String) obj);
		} else if (obj instanceof java.util.List) {
			List<?> values = (List<?>) obj;
			for (Object value : values) {
				if (value instanceof java.util.List) {
					Log.INSTANCE.error("List value of el:{} can not be List", el.getName());
				} else {
					obj2Element(value, el);
				}
			}
		} else if (obj instanceof java.util.Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>) obj;
			if (map.isEmpty()) {
				el.setText("");
			} else {
				Iterator<String> iterator = map.keySet().iterator();
				while (iterator.hasNext()) {
					String key = iterator.next();
					Element addElement = el.addElement(key);
					Object value = map.get(key);
					obj2Element(value, addElement);
				}
			}
		} else {
			try {
				pojo2Element(obj, el);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}

	public void pojo2Element(Object pojo, Element e) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Method methods[] = pojo.getClass().getMethods();
		for (Method method : methods) {
			String name = method.getName();
			if (name.startsWith("get") && !name.equals("getClass")) {
				Object value = method.invoke(pojo, new Object[] {});
				Element el = e.addElement(name.substring(3));
				obj2Element(value, el);
			}
		}
	}

	/**
	 * @see 此方法假设 元素下如果是List元素 则全部是同名List元素
	 * @param el
	 * @param parentMap
	 */
	public static void element2Map(Element el, Map<String, Object> parentMap, boolean islist) {
		element2Map(el, parentMap, islist, false);
	}

	/**
	 * @see 此方法假设 元素下如果是List元素 则全部是同名List元素
	 * @param el
	 * @param parentMap
	 */
	@SuppressWarnings("unchecked")
	public static void element2Map(Element el, Map<String, Object> parentMap, boolean islist, boolean caseSensitive) {
		String name = el.getName();

		List<Element> elements = el.elements();
		if (elements.size() > 0) {// 有子元素
			// 查询同名元素
			List<Element> sames = el.elements(elements.get(0).getName());
			if (sames.size() > 1) {// 多个同名元素
				ArrayList<Map<String, Object>> arrayList = new ArrayList<Map<String, Object>>();
				parentMap.put(name, arrayList);
				for (Element element : sames) {
					Map<String, Object> hashMap = null;
					if (caseSensitive) {
						hashMap = new HashMap<String, Object>();
					} else {
						hashMap = new LinkedCaseInsensitiveMap<Object>();
					}
					arrayList.add(hashMap);
					element2Map(element, hashMap, true, caseSensitive);
				}
			} else {
				Map<String, Object> hashMap = null;
				if (islist) {
					hashMap = parentMap;
				} else {
					hashMap = null;
					if (caseSensitive) {
						hashMap = new HashMap<String, Object>();
					} else {
						hashMap = new LinkedCaseInsensitiveMap<Object>();
					}
					parentMap.put(name, hashMap);
				}
				for (Element element : elements) {
					element2Map(element, hashMap, false, caseSensitive);
				}
			}
		} else {
			parentMap.put(name, el.getText());
		}
	}

	public static Object element2Bean(Element el) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String className = el.attributeValue("class");
		Class<?> clazz = Class.forName(className);
		Object newInstance = clazz.newInstance();
		Method methods[] = clazz.getMethods();
		for (Method method : methods) {
			String name = method.getName();
			if (name.startsWith("set")) {
				@SuppressWarnings("unchecked")
				List<Element> list = el.elements("property");
				for (Element e : list) {
					String feildName = e.attributeValue("name");
					String keyMethodName = new StringBuffer("set").append(feildName).toString();
					if (keyMethodName.toUpperCase().equals(name.toUpperCase())) {
						method.invoke(newInstance, new Object[] { e.attributeValue("value") });
					}
				}
			}
		}
		return newInstance;
	}

}
